﻿using System;
using System.Collections;
using System.Data.SqlTypes;
using System.DirectoryServices.Protocols;
using ActiveDirectoryUtilities.DirectoryServiceAccess;
using AccessActiveDirectory;
using Microsoft.SqlServer.Server;
using System.Security.Principal;
using System.Diagnostics;

public partial class UserDefinedFunctions
{
	[SqlFunction(FillRowMethodName = "Fill_fn_ComputerObjectsRow", DataAccess = DataAccessKind.Read
		, TableDefinition =
		//class general columns
		//class general columns
		"objectGuid UNIQUEIDENTIFIER"
		+ ", distinguishedName NVARCHAR(MAX)"
		+ ", sAMAccountName NVARCHAR(MAX)"
		+ ", lastLogonTimestamp DATETIME"
		+ ", pwdLastSet DATETIME"
		+ ", userAccountControl INT"
		+ ", ADS_UF_SCRIPT BIT"
		+ ", ADS_UF_ACCOUNTDISABLE BIT"
		+ ", ADS_UF_HOMEDIR_REQUIRED BIT"
		+ ", ADS_UF_LOCKOUT BIT"
		+ ", ADS_UF_PASSWD_NOTREQD BIT"
		+ ", ADS_UF_PASSWD_CANT_CHANGE BIT"
		+ ", ADS_UF_ENCRYPTED_TEXT_PWD_ALLOWED BIT"
		+ ", ADS_UF_TEMP_DUPLICATE_ACCOUNT BIT"
		+ ", ADS_UF_NORMAL_ACCOUNT BIT"
		+ ", ADS_UF_INTERDOMAIN_TRUST_ACCOUNT BIT"
		+ ", ADS_UF_WORKSTATION_TRUST_ACCOUNT BIT"
		+ ", ADS_UF_SERVER_TRUST_ACCOUNT BIT"
		+ ", ADS_UF_DONT_EXPIRE_PASSWORD BIT"
		+ ", ADS_UF_MNS_LOGON_ACCOUNT BIT"
		+ ", ADS_UF_SMARTCARD_REQUIRED BIT"
		+ ", ADS_UF_TRUSTED_FOR_DELEGATION BIT"
		+ ", ADS_UF_NOT_DELEGATED BIT"
		+ ", ADS_UF_USE_DES_KEY_ONLY BIT"
		+ ", ADS_UF_DONT_REQ_PREAUTH BIT"
		+ ", ADS_UF_PASSWORD_EXPIRED BIT"
		+ ", ADS_UF_TRUSTED_TO_AUTH_FOR_DELEGATION BIT"
		+ ", ADS_UF_PARTIAL_SECRETS_ACCOUNT BIT"

		//Category specific columns
		+ ", operatingSystem NVARCHAR(MAX)"
		+ ", operatingSystemServicePack NVARCHAR(MAX)"
		+ ", operatingSystemVersion NVARCHAR(MAX)"
		)]
	public static IEnumerable fn_GetComputerObjects(
		SqlString connectionPoint, SqlInt32 portNumber, SqlString baseDn, SqlString filter, SqlString searchScope)
	{
		EventLogTraceListener eltl = new EventLogTraceListener("Application");
		if (ConfiguredSettings.TraceLevel >= 4)
		{
			Trace.Listeners.Add(eltl);
		}

		WindowsImpersonationContext executionContext = null;
		try
		{
			WindowsIdentity callerId = SqlContext.WindowsIdentity;
			if (callerId != null)
			{
				executionContext = callerId.Impersonate();

				string[] attrs = { "objectGuid"
							, "distinguishedName"
							, "samAccountName"
							, "operatingSystem"
							, "operatingSystemServicePack"
							, "operatingSystemVersion"
							, "lastLogonTimestamp"
							, "pwdLastSet"
							, "userAccountControl"
						 };

				#region Check inputs
				if ((connectionPoint.IsNull) || (connectionPoint.CompareTo("") == 0))
				{
					throw new ApplicationException("ConnectionPoint parameter can not be null or blank.");
				}

				if (portNumber.IsNull)
				{
					throw new ApplicationException("PortNumber parameter can not be null.");
				}

				string internalBaseDn = null;
				if ((baseDn.IsNull) || (baseDn.CompareTo("") == 0))
				{
					//TODO:  Update to assume root of current domain
					if ((portNumber.Value == (int)LDAP_PORTS.Global_Catalog) || (portNumber.Value == (int)LDAP_PORTS.Secure_Global_Catalog))
					{
						internalBaseDn = string.Empty;
					}
					else
					{
						throw new ApplicationException("baseDn parameter can not be null or blank.");
					}

				}
				else
				{
					internalBaseDn = (string)baseDn;
				}

				string internalFilter = filter.IsNull ? string.Empty : (string)filter.Value;
				if (string.IsNullOrEmpty(internalFilter))
				{
					internalFilter = "(objectCategory=computer)";
				}
				else if (!internalFilter.ToLowerInvariant().Contains("(objectcategory=computer)"))
				{
					internalFilter = "(&(objectCategory=computer)(" + filter.ToString() + "))";
				}

				string internalScopeString = searchScope.IsNull ? string.Empty : (string)searchScope;
				System.DirectoryServices.Protocols.SearchScope internalScope;
				switch (internalScopeString.ToLowerInvariant())
				{
					case "base":
						internalScope = System.DirectoryServices.Protocols.SearchScope.Base;
						break;

					case "onelevel":
						internalScope = System.DirectoryServices.Protocols.SearchScope.OneLevel;
						break;

					case "subtree":
						internalScope = System.DirectoryServices.Protocols.SearchScope.Subtree;
						break;

					case "":
						internalScope = System.DirectoryServices.Protocols.SearchScope.Subtree;
						break;

					default:
						throw new ApplicationException("Invalid search scope.  Allowed values are Base, OneLevel, Subtree, and NULL.");
				}
				#endregion

				try
				{
					WLdapSearchRequest wsr =
						new WLdapSearchRequest((string)connectionPoint
							, (int)portNumber.Value
							, internalBaseDn
							, internalFilter
							, internalScope
							, attrs);
					wsr.LdapSearchClientTimeOut = ConfiguredSettings.LdapClientTimeout;

					return wsr.FindAll();
				}
				catch (LdapException ex)
				{
					if (ex.ErrorCode == 81)
					{
						throw new ApplicationException("Could not contact domain:  " + (string)connectionPoint);
					}
					else
					{
						SharedLogic.LogTraceInfo(eltl, ex, TraceLevel.Error);
						throw ex;
					}
				}
				catch (Exception ex)
				{
					SharedLogic.LogTraceInfo(eltl, ex, TraceLevel.Error);
					throw ex;
				}

				throw new ApplicationException("Invalid codepath traversed.  Caller should never make it here.");
			}
		}
		catch (Exception ex)
		{
			if (!Trace.Listeners.Contains(eltl))
			{
				Trace.Listeners.Add(eltl);
			}
			Trace.TraceError("Exception Type:\t{0}\n\rError Message:\t{1}\n\rStack:\t{2}", ex.GetType().ToString(), ex.Message, ex.StackTrace);
			//Consider removing eltl if it was added in this module
			throw ex;
		}
		finally
		{
			if (Trace.Listeners.Contains(eltl))
			{
				Trace.Listeners.Remove(eltl);
			}
			if (executionContext != null)
			{
				executionContext.Undo();
			}
		}
		return null;
	}

	private static void Fill_fn_ComputerObjectsRow(
		object o
		, out SqlGuid objectGuid
		, out SqlString distinguishedName
		, out SqlString sAMAccountName
		, out SqlDateTime lastLogonTimestamp
		, out SqlDateTime pwdLastSet
		, out SqlInt32 userAccountControl
		, out SqlBoolean ADS_UF_SCRIPT
		, out SqlBoolean ADS_UF_ACCOUNTDISABLE
		, out SqlBoolean ADS_UF_HOMEDIR_REQUIRED
		, out SqlBoolean ADS_UF_LOCKOUT
		, out SqlBoolean ADS_UF_PASSWD_NOTREQD
		, out SqlBoolean ADS_UF_PASSWD_CANT_CHANGE
		, out SqlBoolean ADS_UF_ENCRYPTED_TEXT_PWD_ALLOWED
		, out SqlBoolean ADS_UF_TEMP_DUPLICATE_ACCOUNT
		, out SqlBoolean ADS_UF_NORMAL_ACCOUNT
		, out SqlBoolean ADS_UF_INTERDOMAIN_TRUST_ACCOUNT
		, out SqlBoolean ADS_UF_WORKSTATION_TRUST_ACCOUNT
		, out SqlBoolean ADS_UF_SERVER_TRUST_ACCOUNT
		, out SqlBoolean ADS_UF_DONT_EXPIRE_PASSWD
		, out SqlBoolean ADS_UF_MNS_LOGON_ACCOUNT
		, out SqlBoolean ADS_UF_SMARTCARD_REQUIRED
		, out SqlBoolean ADS_UF_TRUSTED_FOR_DELEGATION
		, out SqlBoolean ADS_UF_NOT_DELEGATED
		, out SqlBoolean ADS_UF_USE_DES_KEY_ONLY
		, out SqlBoolean ADS_UF_DONT_REQ_PREAUTH
		, out SqlBoolean ADS_UF_PASSWORD_EXPIRED
		, out SqlBoolean ADS_UF_TRUSTED_TO_AUTH_FOR_DELEGATION
		, out SqlBoolean ADS_UF_PARTIAL_SECRETS_ACCOUNT

		//Category specific
		, out SqlString operatingSystem
		, out SqlString operatingSystemServicePack
		, out SqlString operatingSystemVersion
		)
	{
		SearchResultEntry sre = (SearchResultEntry)o;

		if (sre.Attributes.Contains("operatingSystem"))
		{
			operatingSystem = sre.Attributes["operatingSystem"][0].ToString();
		}
		else
		{
			operatingSystem = SqlString.Null;
		}

		if (sre.Attributes.Contains("operatingSystemServicePack"))
		{
			operatingSystemServicePack = sre.Attributes["operatingSystemServicePack"][0].ToString();
		}
		else
		{
			operatingSystemServicePack = SqlString.Null;
		}

		if (sre.Attributes.Contains("operatingSystemVersion"))
		{
			operatingSystemVersion = sre.Attributes["operatingSystemVersion"][0].ToString();
		}
		else
		{
			operatingSystemVersion = SqlString.Null;
		}

		sQLSharedLogic.objectClass_User_SharedLogic(
			sre
			, out objectGuid
			, out distinguishedName
			, out sAMAccountName
			, out lastLogonTimestamp
			, out pwdLastSet
			, out userAccountControl
			, out ADS_UF_SCRIPT
			, out ADS_UF_ACCOUNTDISABLE
			, out ADS_UF_HOMEDIR_REQUIRED
			, out ADS_UF_LOCKOUT
			, out ADS_UF_PASSWD_NOTREQD
			, out ADS_UF_PASSWD_CANT_CHANGE
			, out ADS_UF_ENCRYPTED_TEXT_PWD_ALLOWED
			, out ADS_UF_TEMP_DUPLICATE_ACCOUNT
			, out ADS_UF_NORMAL_ACCOUNT
			, out ADS_UF_INTERDOMAIN_TRUST_ACCOUNT
			, out ADS_UF_WORKSTATION_TRUST_ACCOUNT
			, out ADS_UF_SERVER_TRUST_ACCOUNT
			, out ADS_UF_DONT_EXPIRE_PASSWD
			, out ADS_UF_MNS_LOGON_ACCOUNT
			, out ADS_UF_SMARTCARD_REQUIRED
			, out ADS_UF_TRUSTED_FOR_DELEGATION
			, out ADS_UF_NOT_DELEGATED
			, out ADS_UF_USE_DES_KEY_ONLY
			, out ADS_UF_DONT_REQ_PREAUTH
			, out ADS_UF_PASSWORD_EXPIRED
			, out ADS_UF_TRUSTED_TO_AUTH_FOR_DELEGATION
			, out ADS_UF_PARTIAL_SECRETS_ACCOUNT
			);
	}
};

